package com.example.basic.web.service.login.impl;

import com.example.basic.exception.ServiceException;
import com.example.basic.utils.*;
import com.example.basic.web.enums.EnableTypeEnum;
import com.example.basic.web.enums.YesNoEnum;
import com.example.basic.web.model.user.User;
import com.example.basic.web.service.common.CommonService;
import com.example.basic.web.service.login.LoginService;
import com.example.basic.web.service.menu.MenuService;
import com.example.basic.web.service.redis.IRedisService;
import com.example.basic.web.service.user.UserService;
import com.example.basic.web.vo.login.LoginParams;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

import static com.example.basic.utils.CheckUtil.notNull;
import static com.example.basic.utils.JwtUtil.HEADER_STRING;
import static com.example.basic.utils.JwtUtil.TOKEN_PREFIX;


@Service
@Slf4j
public class LoginServiceImpl implements LoginService {

    @Resource
    private HttpServletRequest request;

    @Resource
    private HttpServletResponse response;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private UserService userService;


    @Value("${token.expTime}")
    private long tokenExpTime;

    @Autowired
    private IRedisService iRedisService;

    @Resource
    private CommonService commonService;

    @Resource
    private MenuService menuService;



    /**
     * @param loginName 登录名
     * @param loginPwd  密码
     * @param sysType
     * @return
     * @create syp 登录
     */
    @Override
    public LoginParams login(String loginName, String loginPwd, String sysType) throws Exception {

        notNull(loginName,"登录名不能为空");
        notNull(loginPwd,"密码不能为空");
        notNull(sysType,"系统类型不能为空");

        // 登录错误标识，用于更新redis中的信息
        String loginErrorCount = "login_error_count";

        // 用户登录模型,用于登录成功后返回前段信息
        LoginParams loginParams = new LoginParams();

        //根据登录账号查询用户
        User user = userService.queryUserByLoginName(loginName);
        //校验用户信息
        judgeUserInfo(user);
        // 校验用户密码
//        judgeloginPwd(loginName, loginPwd, user,loginErrorCount);
        //将一些必要的值返回给前端展示
        copyValue(user,loginParams);
        // 生成token信息
        generateTokenInfo(loginParams,loginErrorCount);

        if(loginName.equals("0000")){
            user.setRoleCode(null);
        }

        List<Map<String, Object>> sysMenuList = new ArrayList<>();
        //查询用户有权限的菜单
        sysMenuList = menuService.queryListByRoleCode(user.getRoleCode(),sysType);

        loginParams.setMenuList(sysMenuList);
        return loginParams;
    }

    /**
     * 将一些必要的值返回给前端展示
     * @param user
     * @param loginParams
     */
    private void copyValue(User user, LoginParams loginParams) {
        loginParams.setId(user.getId());
        loginParams.setUserName(user.getUserName());
        loginParams.setLoginName(user.getLoginName());
        loginParams.setUserCode(user.getUserCode());
        loginParams.setRole(user.getRoleCode());
        loginParams.setBelongDept(user.getBelongDept());
        loginParams.setResponsibleDept(user.getResponsibleDept());

    }

    /**
     * 校验用户信息
     * @param user
     */
    private void judgeUserInfo(User user) {
        if (user == null) {
            throw new ServiceException("用户不存在");
        }
        if(user.getEnableFlag().equals(EnableTypeEnum.OFF.getType())){
            throw new ServiceException("用户已停用");
        }
        if(StringUtils.emptyParams(user.getRoleCode())){
            throw new ServiceException("用户未维护角色");
        }
        if(StringUtils.emptyParams(user.getBelongDept())){
            throw new ServiceException("用户未维护所属部门");
        }
        if(StringUtils.emptyParams(user.getResponsibleDept())){
            throw new ServiceException("用户未维护责任部门");
        }
        if(user.getLockFlag().equals(YesNoEnum.YES_FLAG.getType())){
            throw new ServiceException("用户已锁定");
        }
    }

    /**
     * @create syp 20200521 校验用户密码
     * @param loginName 登录名
     * @param loginPwd 密码
     */
    private void judgeloginPwd(String loginName, String loginPwd, User user, String loginErrorCount) {
        // 校验密码
        if(loginPwd.equals(user.getLoginPwd())){
            return;
        }

        Long errorCount = iRedisService.hIncrby(loginErrorCount, loginName, 1);
        if(errorCount >= 5){
            // 将状态置为锁定状态
            user.setLockFlag("1");
            userService.addAndUpdate(user);
        }

        // 密码不正确
        throw new ServiceException("密码错误");
    }

    /**
     * @create syp 生成token信息
     * @param loginParams 用户登录信息
     */
    private void generateTokenInfo(LoginParams loginParams,String loginErrorCount) {

        // 设置用户登录信息
        Map<String, Object> map = new HashMap<>();
        map.put("userCode", loginParams.getUserCode());
        map.put("userName", loginParams.getUserName());
        map.put("loginName", loginParams.getLoginName());
        map.put("expiryTime",System.currentTimeMillis() + tokenExpTime);
        map.put("tokenExpTime",tokenExpTime);
        String jwt = JwtUtil.generateToken(map);
        response.addHeader(HEADER_STRING, TOKEN_PREFIX + jwt);

        //登录成功，连续登录错误次数清零
        iRedisService.hset(loginErrorCount, loginParams.getLoginName(), String.valueOf(0));

        // 设置token样式，用于更新redis中的信息
        String loginTokenKey = "login_token_" + loginParams.getLoginName();
        //登录成功，将token放到redis，后续为了做同一客户端防双开，不同客户端允许同时运行
        redisUtil.hset(loginTokenKey, loginParams.getLoginName(), TOKEN_PREFIX + jwt, tokenExpTime);
        //应前端需求，把token放置在用户登录模型中
        loginParams.setToken(jwt);
    }

    /**
     * 退出
     * @param loginName
     * @return
     * @throws Exception
     */
    @Override
    public boolean loginOut(String loginName) throws Exception {

        notNull(loginName, "登录名不能为空");
        //从请求头中取出token
        String token = request.getHeader("Authorization");
        // 验证token
        Map<String, Object> stringObjectMap = JwtUtil.validateToken(token);

        String loginTokenKey = "login_token_"+ loginName;
        // 获取redis中的token数据
        String tokenFromRedis = (String)redisUtil.hget(loginTokenKey, loginName);

        // 比较前段传入的token是否和redis中的token一致（登录接口已做放双开操作，此处不需要额外处理）
        if (((token.replace(TOKEN_PREFIX, "")).trim()).equals((tokenFromRedis.replace(TOKEN_PREFIX, "")).trim())) {
            // 删除用户的token
            redisUtil.hdel(loginTokenKey, loginName);
        }
        return true;
    }
}
